---
title: 面向对象编程基础
---

面向对象编程 (OOP) 是一种基于“对象”概念的编程范式，对象可以包含数据和代码。C++ 是一种多范式语言，对 OOP 有强大的支持，而 JavaScript 使用基于原型的对象模型。

## 类和对象定义

在 C++ 中，**类**是创建对象的蓝图，为状态（成员变量）提供初始值，并实现行为（成员函数）。**对象**是类的一个实例。

<UniversalEditor title="类和对象定义比较" compare={true}>
```javascript !! js
// JavaScript: 对象字面量和构造函数
// 对象字面量
const car1 = {
  brand: "Toyota",
  model: "Camry",
  start: function() {
    console.log(`${this.brand} ${this.model} 启动了。`);
  }
};
car1.start();

// 构造函数
function Car(brand, model) {
  this.brand = brand;
  this.model = model;
  this.start = function() {
    console.log(`${this.brand} ${this.model} 启动了。`);
  };
}
const car2 = new Car("Honda", "Civic");
car2.start();

// ES6 Class Syntax (原型上的语法糖)
class Vehicle {
  constructor(brand, model) {
    this.brand = brand;
    this.model = model;
  }
  start() {
    console.log(`${this.brand} ${this.model} 启动了。`);
  }
}
const car3 = new Vehicle("Ford", "Focus");
car3.start();
```

```cpp !! cpp
// C++: 类和对象定义
#include <iostream>
#include <string>

class Car { // 类定义
public: // 访问修饰符
  std::string brand; // 成员变量
  std::string model;

  void start() { // 成员函数
    // std::cout << brand << " " << model << " 启动了。" << std::endl;
  }
};

int main() {
  Car car1; // 创建 Car 类的对象 (实例)
  car1.brand = "Toyota";
  car1.model = "Camry";
  car1.start();

  Car car2; // 另一个对象
  car2.brand = "Honda";
  car2.model = "Civic";
  car2.start();

  return 0;
}
```
</UniversalEditor>

## 构造函数和析构函数

**构造函数**是对象创建时自动调用的特殊成员函数。它们用于初始化对象的状态。

**析构函数**是对象销毁时（超出作用域或被 `delete`）自动调用的特殊成员函数。它们用于清理资源（例如，释放动态分配的内存）。

<UniversalEditor title="构造函数和析构函数比较" compare={true}>
```javascript !! js
// JavaScript: 构造函数和类构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
  console.log(`${this.name} 已创建。`);
}
const p1 = new Person("Alice", 30);
// 没有显式析构函数；垃圾回收器处理清理

class Animal {
  constructor(name) {
    this.name = name;
    console.log(`${this.name} (Animal) 已创建。`);
  }
  // 没有显式析构函数
}
const a1 = new Animal("Leo");
```

```cpp !! cpp
// C++: 构造函数和析构函数
#include <iostream>
#include <string>

class Person {
public:
  std::string name;
  int age;

  // 构造函数
  Person(std::string n, int a) : name(n), age(a) {
    // std::cout << name << " (Person) 已创建。" << std::endl;
  }

  // 析构函数
  ~Person() {
    // std::cout << name << " (Person) 已销毁。" << std::endl;
  }
};

int main() {
  Person p1("Alice", 30); // 构造函数被调用
  // p1 在此超出作用域，析构函数自动被调用

  Person* p2 = new Person("Bob", 25); // 构造函数被调用
  delete p2; // 析构函数显式被调用

  return 0;
}
```
</UniversalEditor>

## 封装和访问控制

**封装**是将数据（成员变量）和操作数据的方法（成员函数）捆绑到一个单元（类）中，并限制对对象某些组件的直接访问。这通过**访问修饰符**实现：

*   `public`：成员可从任何地方访问。
*   `private`：成员只能从类内部访问。
*   `protected`：成员可从类内部和派生类访问。

<UniversalEditor title="封装比较" compare={true}>
```javascript !! js
// JavaScript: 封装 (使用闭包或 #private 字段)
class BankAccount {
  #balance; // 私有字段 (ES2019+)

  constructor(initialBalance) {
    this.#balance = initialBalance;
  }

  deposit(amount) {
    if (amount > 0) {
      this.#balance += amount;
      console.log(`已存入: ${amount}。新余额: ${this.#balance}`);
    }
  }

  getBalance() {
    return this.#balance;
  }
}

const myAccount = new BankAccount(100);
myAccount.deposit(50);
// console.log(myAccount.#balance); // SyntaxError: 私有字段 '#balance' 必须在封闭类中声明
console.log(myAccount.getBalance()); // 150
```

```cpp !! cpp
// C++: 带有访问修饰符的封装
#include <iostream>

class BankAccount {
private: // 私有成员变量
  double balance;

public: // 公有成员函数
  BankAccount(double initialBalance) : balance(initialBalance) {}

  void deposit(double amount) {
    if (amount > 0) {
      balance += amount;
      // std::cout << "已存入: " << amount << ". 新余额: " << balance << std::endl;
    }
  }

  double getBalance() const { // const 表示它不修改对象状态
    return balance;
  }
};

int main() {
  BankAccount myAccount(100);
  myAccount.deposit(50);
  // myAccount.balance = 1000; // 错误: 'balance' 是私有的
  // std::cout << myAccount.getBalance() << std::endl; // 150
  return 0;
}
```
</UniversalEditor>

## 基本继承

**继承**是一种机制，其中从现有类（基类或超类）创建新类（派生类或子类）。派生类继承基类的属性和行为，促进代码重用。

<UniversalEditor title="基本继承比较" compare={true}>
```javascript !! js
// JavaScript: 类继承
class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(`${this.name} 发出声音。`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 调用父类构造函数
    this.breed = breed;
  }
  speak() {
    console.log(`${this.name} (${this.breed}) 吠叫。`);
  }
}

const myDog = new Dog("Buddy", "金毛寻回犬");
myDog.speak(); // Buddy (金毛寻回犬) 吠叫。
```

```cpp !! cpp
// C++: 基本继承
#include <iostream>
#include <string>

class Animal { // 基类
public:
  std::string name;
  Animal(std::string n) : name(n) {}
  void speak() {
    // std::cout << name << " 发出声音。" << std::endl;
  }
};

class Dog : public Animal { // 派生类，公有继承
public:
  std::string breed;
  Dog(std::string n, std::string b) : Animal(n), breed(b) {} // 调用基类构造函数
  void speak() { // 覆盖基类方法
    // std::cout << name << " (" << breed << ") 吠叫。" << std::endl;
  }
};

int main() {
  Dog myDog("Buddy", "金毛寻回犬");
  myDog.speak();
  return 0;
}
```
</UniversalEditor>

## 多态概念

**多态**（意为“多种形式”）允许不同类的对象被视为共同基类的对象。在 C++ 中，多态主要通过**虚函数**和**指向基类的指针/引用**实现。

<UniversalEditor title="多态概念比较" compare={true}>
```javascript !! js
// JavaScript: 多态 (通过动态类型和原型自然实现)
class Shape {
  draw() {
    console.log("绘制一个通用形状。");
  }
}

class Circle extends Shape {
  draw() {
    console.log("绘制一个圆形。");
  }
}

class Rectangle extends Shape {
  draw() {
    console.log("绘制一个矩形。");
  }
}

const shapes = [new Shape(), new Circle(), new Rectangle()];

shapes.forEach(shape => shape.draw());
// 输出:
// 绘制一个通用形状。
// 绘制一个圆形。
// 绘制一个矩形。
```

```cpp !! cpp
// C++: 带有虚函数的多态
#include <iostream>

class Shape { // 基类
public:
  virtual void draw() { // 虚函数
    // std::cout << "绘制一个通用形状。" << std::endl;
  }
};

class Circle : public Shape { // 派生类
public:
  void draw() override { // override 关键字 (C++11) 用于清晰度
    // std::cout << "绘制一个圆形。" << std::endl;
  }
};

class Rectangle : public Shape { // 派生类
public:
  void draw() override {
    // std::cout << "绘制一个矩形。" << std::endl;
  }
};

int main() {
  Shape* s1 = new Shape();
  Shape* s2 = new Circle(); // 基类指针指向派生类对象
  Shape* s3 = new Rectangle();

  s1->draw(); // 调用 Shape::draw()
  s2->draw(); // 调用 Circle::draw() (多态行为)
  s3->draw(); // 调用 Rectangle::draw() (多态行为)

  delete s1; delete s2; delete s3;
  return 0;
}
```
</UniversalEditor>

## 与 JavaScript 原型链的比较

JavaScript 的对象模型是基于原型的，而不是传统意义上的基于类（尽管 ES6 类提供了语法糖）。当你尝试访问对象上的属性或方法时，JavaScript 会首先直接在对象上查找。如果找不到，它会在对象的原型上查找，然后在原型的原型上查找，依此类推，形成一个**原型链**。

**主要差异：**
*   **继承模型：** C++ 使用经典继承（基于类），而 JavaScript 使用原型继承。
*   **显式 vs. 隐式：** C++ 继承是显式的，带有 `class` 和 `public/private/protected` 等关键字。JavaScript 的原型继承更隐式。
*   **多态：** C++ 依赖虚函数和基类指针/引用来实现运行时多态。JavaScript 由于其动态特性和原型链而自然实现多态。

## 虚函数机制

在 C++ 中，**虚函数**是在基类中声明并由派生类重新定义（覆盖）的成员函数。当你通过指向基类的指针或引用调用虚函数时，执行哪个特定版本的函数是在运行时根据所指向或引用的对象的实际类型确定的。这称为**动态分派**或**运行时多态**。

*   **`virtual` 关键字：** 声明一个函数为基类中的虚函数。
*   **`override` 关键字 (C++11)：** 可选，但在派生类中是个好习惯，用于指示函数旨在覆盖基类中的虚函数。如果签名不匹配，有助于捕获错误。
*   **虚表 (vtable)：** 编译器通常使用虚表来实现虚函数，虚表是函数指针的查找表。

---

### 练习题：
1.  解释 C++ 中封装和继承的概念。访问修饰符 (`public`、`private`、`protected`) 如何与封装相关？
2.  C++ 中的多态是什么，以及如何使用虚函数实现它？提供一个简单的 C++ 代码示例，演示运行时多态。
3.  比较和对比 C++ 的基于类的继承与 JavaScript 的基于原型的继承。它们在实现面向对象原则方面的主要差异是什么？

### 项目构想：
*   使用继承和多态在 C++ 中设计并实现一个简单的 `Shape` 层次结构。创建带有虚函数 `calculateArea()` 的基类 `Shape`。从 `Shape` 派生 `Circle` 和 `Rectangle` 类，每个类都覆盖 `calculateArea()` 以提供其特定的实现。然后，创建一个 `Shape` 指针数组，并演示对不同形状多态地调用 `calculateArea()`。